<template lang="html">
  <div
    :style="getNodeStyle"
    :title="title"
    class="node-box"
    :class="{'node-selected': isChoose}"
    @click="click"
    @dblclick="dblclick"
    @mousedown.stop="mousedown"
    @mouseup="mouseup"
    @contextmenu.prevent.stop>
    <!-- node.template 存在则创建对应组件作为节点 否则为普通节点-->
    <template v-if="selfNode.template">
      <component :is="component" v-if="component" :ref="k" :node="selfNode" @nodeDetail="nodeDetail"/>
    </template>
    <template v-else>
  <canvas ref="canvas"
          class="node-box-bg" />
  <div :title="title"
       :style="{'line-height': height * state.baseOptions.pageSize + 'px' }"
       class="node-box-content">
    {{ title }}
  </div>
  <!-- 【0：未执行；1：运行中；2：已成功；3：已失败；4：已跳过】 -->
  <span v-if="runState.isShowTime"
        class="executor-pending">
    {{executorTime}}
  </span>
  <i :title="runState.title"
     :class="runState.colorClass"
     :style="`font-size:${nodeStatusIconSIze}px`"></i>
</template>
    <template v-if="!state.mapMode">
  <div v-for="(arrow, index) in arrows"
       :key="index"
       :style="getNodeAnchorStyle(arrow)"
       class="node-anchor"
       @mousedown.stop="clickAnchor(arrow, $event)" />
</template>

  </div>
</template>
<script>
import Vue from 'vue'
import contentMenu from './contentMenu.js'
import { mapActions, commit, mixin } from './store'
import { findComponentUpward, getKey } from './util.js'

export default {
  mixins: [mixin],
  props: {
    k: {
      type: [String, Number],
      required: true
    },
    type: {
      type: String,
      required: true
    },
    title: {
      type: String,
      required: true
    },
    x: {
      type: Number,
      required: true
    },
    y: {
      type: Number,
      required: true
    },
    height: {
      type: Number,
      required: true
    },
    width: {
      type: Number,
      required: true
    },
    borderWidth: {
      type: Number,
      required: true
    },
    borderColor: {
      type: String,
      required: true
    },
    radiusWidth: {
      type: Number,
      required: true
    },
    anchorSize: {
      type: Number,
      required: true
    },
    image: {
      type: String,
      required: true
    },
    runState: {
      type: [Object],
      default () {
        return {}
      }
    },
    selected: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      component: null,
      currentBorderColor: this.borderColor,
      arrows: ['left', 'top', 'right', 'bottom'],
      isChoose: this.selected
    }
  },
  computed: {
    getNodeStyle () {
      let styelObj = {
        'left': this.x * this.state.baseOptions.pageSize + 'px',
        'top': this.y * this.state.baseOptions.pageSize + 'px',
        'height': this.height * this.state.baseOptions.pageSize + 'px',
        'width': this.width * this.state.baseOptions.pageSize + 'px'
      }
      if (this.nodeType === 'canvas') {
        styelObj = {
          ...styelObj,
          'background-image': `url(${this.image})`,
          'background-repeat': 'no-repeat',
          'background-position': '5% center',
          'background-size': '20%'
        }
      }
      return {
        ...styelObj,
        ...this.runState.nodeStyle
      }
    },
    // nodeIcon () {
    //   debugger
    //   const iconType = this.runState.iconType
    //   let iconClass = { ...this.runState.colorClass, 'executor-icon': true }
    //   iconClass[iconType] = true
    //   return iconClass
    // },
    getImageStyle () {
      return {
        transform: `translate(-50%, -50%) scale(${this.state.baseOptions.pageSize})`
      }
    },
    selfNode () {
      return this.getNodeByKey(this.k)
    },
    nodeType () {
      if (this.selfNode && this.selfNode.template) {
        return 'html'
      }
      return 'canvas'
    },
    executorTime () {
      return this.runState.time
    },
    nodeStatusIconSIze () {
      return 20 * this.state.baseOptions.pageSize
    }
  },
  watch: {
    'runState': {
      handler () {
        if (this.runState.borderColor) {
          this.currentBorderColor = this.runState.borderColor
        }
      },
      deep: true
    },
    width () {
      if (this.nodeType === 'canvas') {
        this.initCanvas()
      }
    },
    selected (v) {
      this.isChoose = v
    }
  },
  created () {
    // node.template 为string 或 vue.extend 组件构造器
    if (this.nodeType === 'html' && typeof this.selfNode.template === 'string') {
      this.component = Vue.component(`html-node-${this.k}`, {
        props: {
          node: {
            type: Object,
            default: () => ({})
          }
        },
        template: this.selfNode.template
      })
    } else if (typeof this.selfNode.template === 'function') {
      this.component = Vue.component(`html-node-${this.k}`, this.selfNode.template)
    }
  },
  mounted () {
    this.designer = findComponentUpward(this, 'Designer')
    this.shape = null
    this.designer.myShapes.forEach(item => {
      if (item.type === this.type) {
        this.shape = item
      } else {
        if (item.children && item.children.length) {
          item.children.forEach(son => {
            if (son.type === this.type) {
              this.shape = son
            }
          })
        }
      }
    })
    if (this.nodeType === 'html') { return }
    this.initCanvas()
    this.$watch('state.baseOptions.pageSize', function () {
      this.initCanvas()
    })
    this.$watch('currentBorderColor', function () {
      this.initCanvas()
    })
    this.$watch('state.choosing', function (v) {
      if (v && v.type === 'node') {
        this.isChoose = v.key.indexOf(this.k) > -1
      }
    })
  },
  beforeDestroy () {
    if (this.contentMenu) {
      this.contentMenu.destroy()
      this.contentMenu = null
    }
  },
  methods: {
    ...mapActions(['setDraging', 'clearDraging', 'getNodeByKey']),
    initCanvas () {
      if (!this.canvasCtx) {
        this.canvas = this.$refs.canvas
        this.canvasCtx = this.canvas.getContext('2d')
      }
      // 设置canvas宽度和高度
      this.canvas.height = this.height * this.state.baseOptions.pageSize
      this.canvas.width = this.width * this.state.baseOptions.pageSize
      let ctx = this.canvasCtx
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
      ctx.lineWidth = this.borderWidth * this.state.baseOptions.pageSize
      ctx.strokeStyle = this.currentBorderColor
      ctx.lineCap = 'round'
      ctx.lineJoin = 'round'
      this.roundedRect(
        ctx,
        this.borderWidth / 2 * this.state.baseOptions.pageSize,
        this.borderWidth / 2 * this.state.baseOptions.pageSize,
        (this.width - this.borderWidth) * this.state.baseOptions.pageSize,
        (this.height - this.borderWidth) * this.state.baseOptions.pageSize,
        this.radiusWidth * this.state.baseOptions.pageSize
      )
    },
    roundedRect (ctx, x, y, width, height, radius) {
      ctx.beginPath()
      ctx.moveTo(x, y + radius)
      ctx.lineTo(x, y + height - radius)
      ctx.quadraticCurveTo(x, y + height, x + radius, y + height)
      ctx.lineTo(x + width - radius, y + height)
      ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius)
      ctx.lineTo(x + width, y + radius)
      ctx.quadraticCurveTo(x + width, y, x + width - radius, y)
      ctx.lineTo(x + radius, y)
      ctx.quadraticCurveTo(x, y, x, y + radius)
      ctx.stroke()
    },

    // 要考虑到盒子模型
    getNodeAnchorStyle (arrow) {
      let anchorWidth = this.anchorSize * this.state.baseOptions.pageSize
      let boxWidth = this.width * this.state.baseOptions.pageSize
      let boxHight = this.height * this.state.baseOptions.pageSize
      let boxBorderWidth = this.borderWidth * this.state.baseOptions.pageSize
      let style = {
        'height': anchorWidth + 'px',
        'width': anchorWidth + 'px',
        'border-radius': anchorWidth / 2 + 'px'
      }
      if (arrow === 'top') {
        return Object.assign(style, {
          left: boxWidth / 2 - anchorWidth / 2 + 'px',
          top: -anchorWidth / 2 + boxBorderWidth / 2 + 'px'
        })
      }
      if (arrow === 'bottom') {
        return Object.assign(style, {
          left: boxWidth / 2 - anchorWidth / 2 + 'px',
          top: boxHight - anchorWidth / 2 - boxBorderWidth / 2 + 'px'
        })
      }
      if (arrow === 'left') {
        return Object.assign(style, {
          left: -anchorWidth / 2 + boxBorderWidth / 2 + 'px',
          top: (boxHight - anchorWidth) / 2 + 'px'
        })
      }
      if (arrow === 'right') {
        return Object.assign(style, {
          left: boxWidth - anchorWidth / 2 - boxBorderWidth / 2 + 'px',
          top: (boxHight - anchorWidth) / 2 + 'px'
        })
      }
    },
    showMenu (e) {
      if (this.contentMenu) {
        this.contentMenu.destroy()
        this.contentMenu = null
      }
      let { defaultMenu, userMenu, beforeShowMenu } = this.designer.myMenuOptions
      let arr = [
        ...defaultMenu
      ]
      if (userMenu && Array.isArray(userMenu)) {
        arr = arr.concat(userMenu)
      }
      if (typeof beforeShowMenu === 'function') {
        arr = beforeShowMenu(this.selfNode, arr, 'node')
        if (Array.isArray(arr)) {
          arr = arr.map((it) => {
            let menuItem = {
              text: it.text,
              value: it.value
            }
            if (it.img) {
              menuItem.img = it.img
            } else if (it.icon) {
              menuItem.icon = it.icon
            }
            return menuItem
          })
        } else {
          console.warn('ctxMenuOptions.beforeShowMenu返回值必须是一个数组')
        }
      }
      if (arr.length < 1) { return }
      this.contentMenu = contentMenu({
        data: arr,
        left: e.clientX,
        top: e.clientY,
        choose: (data) => {
          // if (this.state.disabled) return;
          this.designer.$emit(`ctx-menu-${data.value}`, this.selfNode, 'node')
          console.log('data.value', data.value)
          this.$emit('operat-node', data.value, this.k)
        }
      })
    },
    mousedown (e) {
      this._is_drag = false
      // 点击鼠标左键
      if (e.which === 1) {
        if (this.state.disabled) return
        let keys = []
        if (this.state.choosing.type === 'node') {
          keys = e.ctrlKey || this.state.choosing.key.length > 1 ? this.state.choosing.key : []
        }
        if (keys.indexOf(this.k) < 0) {
          keys.push(this.k)
        }
        let selectedNodesData = {}
        let nodes = []
        this.state.nodes.forEach((node) => {
          if (keys.indexOf(node.key) > -1) {
            selectedNodesData[node.key] = {
              beginX: parseInt(node.x),
              beginY: parseInt(node.y)
            }
            nodes.push(node)
          }
        })
        if (this.designer) {
          commit(this.$store, 'UPDATE_CHOOSING', {
            type: 'node',
            key: keys
          })
        }
        this.setDraging({
          type: 'node',
          data: {
            key: this.key,
            beginX: parseInt(this.x),
            beginY: parseInt(this.y),
            beginPageX: e.pageX,
            beginPageY: e.pageY,
            selectedNodesBegin: selectedNodesData,
            nodes: nodes
          }
        })
      }

      this._mouseBeginX = e.clientX
      this._mouseBeginY = e.clientY
    },
    mouseup (e) {
      // 点击鼠标右键
      if (e.which === 3) {
        this.showMenu(e)
      }
      if (this.state.disabled) return
      this._mouseEndX = e.clientX
      this._mouseEndY = e.clientY

      if (this.isdrag(this._mouseBeginX, this._mouseBeginY, this._mouseEndX, this._mouseEndY)) {
        this._is_drag = true
        e.preventDefault()
        return false
      } else {
        let keys = []
        if (e.ctrlKey && this.state.choosing.type === 'node') {
          keys = this.state.choosing.key || []
        }
        if (keys.indexOf(this.k) < 0) {
          keys.push(this.k)
        }
        if (this.designer) {
          commit(this.$store, 'UPDATE_CHOOSING', {
            type: 'node',
            key: keys
          })
        }
      }
    },
    click (e) {
      if (this.state.disabled) return
      if (this._is_drag || e.ctrlKey) {
        e.preventDefault()
        e.stopPropagation()
        return false
      }
      this.designer.$emit('node-click', this.selfNode)
    },
    isdrag (x1, y1, x2, y2) {
      if (Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) > 4) {
        return true
      }
      return false
    },
    clickAnchor (arrow, e) {
      if (this.state.disabled) return
      let beginNode = this.selfNode
      let endNodeArrows = {
        top: 'bottom',
        bottom: 'top',
        left: 'right',
        right: 'left'
      }
      let endNodeArrow = endNodeArrows[arrow]
      let x = this.x
      let y = this.y
      if (endNodeArrow === 'top') {
        y = y + this.height
      }
      if (endNodeArrow === 'bottom') {
        y = y - this.height
      }
      if (endNodeArrow === 'left') {
        x = x + this.width
      }
      if (endNodeArrow === 'right') {
        x = x - this.width
      }
      let endNode = Object.assign({}, beginNode, {
        key: getKey(),
        x: x,
        y: y
      })
      this.setDraging({
        type: 'link',
        data: {
          key: getKey(),
          beginNode: beginNode,
          beginNodeArrow: arrow,
          endNode: endNode,
          endNodeArrow: endNodeArrow,
          beginX: e.pageX - this.state.baseOptions.nodeViewOffsetX,
          beginY: e.pageY - this.state.baseOptions.nodeViewOffsetY,
          endX: 0,
          endY: 0,
          ...this.state.linkerOptions
        }
      })
    },
    dblclick () {
      if (this.designer) {
        this.designer.$emit('node-dblclick', this.selfNode)
      }
    },

    /**
     * template创建的vue compnent节点盒子layout属性
     */
    getHtmlNodeRect () {
      if (this.$refs[this.k]) {
        let nodeComp = this.$refs[this.k]
        return nodeComp.$el.getBoundingClientRect()
      }
    }
  }
}
</script>
